package com.wsoft.bpm.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wsoft.bpm.constants.BpmConstant;
import com.wsoft.bpm.entity.ActWorkflowConfigEntity;
import com.wsoft.bpm.enums.*;
import com.wsoft.bpm.mapper.ActWorkflowConfigMapper;
import com.wsoft.bpm.node.Process;
import com.wsoft.bpm.node.*;
import com.wsoft.bpm.query.node.*;
import com.wsoft.bpm.service.IActWorkflowConfigService;
import com.wsoft.bpm.vo.VariableVO;
import com.wsoft.constant.CommonConstant;
import com.wsoft.constant.redis.RedisKeyConstant;
import com.wsoft.core.exception.Asserts;
import com.wsoft.core.service.RedisService;
import com.wsoft.entity.BaseEntity;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zhonglj
 * @since 2024年09月12日 10:00:59
 */
@Service
public class ActWorkflowConfigServiceImpl extends ServiceImpl<ActWorkflowConfigMapper, ActWorkflowConfigEntity> implements IActWorkflowConfigService {
    @Resource
    private RedisService redisService;
    @Resource
    private RepositoryService repositoryService;

    @Override
    public String convertXmlStartEvent(StartEventQuery req) {
        // 请求数据转换为xml
        StartEvent startEvent = new StartEvent();
        startEvent.setId(req.getId());
        startEvent.setName(req.getName());
        startEvent.setInitiator(req.getInitiator());
        if (StrUtil.isNotBlank(req.getDocumentation())) {
            startEvent.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
        }

        if (StrUtil.isNotBlank(req.getMessageRef())) {
            startEvent.setMessageEventDefinition(
                    new MessageEventDefinition()
                            .setMessageRef(req.getMessageRef())
            );
        }

        if (StrUtil.isNotBlank(req.getStartCondition())) {
            req.setListeners(addListener(req.getListeners(),
                    ListenerEvent.EXECUTION_START.getValue(),
                    ListenerType.DELEGATE_EXPRESSION.getValue(),
                    BpmConstant.START_PROCESS_ABLE_LISTENER,
                    "流程开始监听器")
            );
        }

        ExtensionElements extensionElements = null;
        if (CollUtil.isNotEmpty(req.getListeners())) {
            extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
            extensionElements.setActivityListeners(
                    req.getListeners().stream()
                            .map(e -> getActivityListener(e))
                            .collect(Collectors.toList())
            );
        }
        startEvent.setExtensionElements(extensionElements);

        return convert(startEvent, startEvent.getId(), req.getModelId(), JSONUtil.toJsonStr(req));
    }

    public List<ListenerQuery> addListener(List<ListenerQuery> list, String event, String type, String value, String listenerName) {
        return addListenerReplace(list, event, type, value, listenerName, false);
    }

    private List<ListenerQuery> addListenerReplace(List<ListenerQuery> list, String event, String type, String value, String listenerName, boolean replace) {
        List<ListenerQuery> listeners = CollUtil.isNotEmpty(list) ? list : CollUtil.newArrayList();
        // 判断是否已经存在
        Optional<ListenerQuery> find = listeners.stream().filter(e -> StrUtil.equals(e.getName(), listenerName)).findFirst();
        if (!find.isPresent()) {
            // 添加监听器
            ListenerQuery auto = new ListenerQuery();
            auto.setEvent(event);
            auto.setType(type);
            auto.setValue(value);
            auto.setName(listenerName);
            listeners.add(auto);

        } else if (replace) {
            // 更新监听器数据
            ListenerQuery updateListener = find.get();
            updateListener.setType(type);
            updateListener.setValue(value);
            updateListener.setEvent(event);
            updateListener.setName(listenerName);
        }

        return listeners;
    }

    private ActivityListener getActivityListener(ListenerQuery query) {
        String type = query.getType();
        String value = query.getValue();
        ActivityListener listener = new ActivityListener();
        listener.setEvent(query.getEvent());
        // 根据不同的监听器类型设置属性
        if (StrUtil.equals(type, ListenerType.JAVA_CLASS.getValue())) {
            listener.setClz(value);
        } else if (StrUtil.equals(type, ListenerType.EXPRESSION.getValue())) {
            listener.setExpression(value);
        } else if (StrUtil.equals(type, ListenerType.DELEGATE_EXPRESSION.getValue())) {
            listener.setDelegateExpression(value);
        } else {
            Asserts.fail("[bpm.def.error.listener.type]");
        }
        return listener;
    }

    @Override
    public String convertXmlSequenceFlow(SequenceFlowQuery req) {
        ExtensionElements extensionElements = null;
        SequenceFlow sequenceFlow = new SequenceFlow();
        sequenceFlow.setId(req.getId());
        sequenceFlow.setName(req.getName());
        sequenceFlow.setSourceRef(req.getSourceRef());
        sequenceFlow.setTargetRef(req.getTargetRef());
        sequenceFlow.setSkipExpression(req.getSkipExpression());
        if (StrUtil.isNotBlank(req.getDocumentation())) {
            sequenceFlow.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
        }

        if (StrUtil.isNotBlank(req.getCondition())) {
            sequenceFlow.setConditionExpression(new FormalExpression()
                    .setTextNode(new TextNode().setText(req.getCondition())));
        }

        if (CollUtil.isNotEmpty(req.getListeners())) {
            extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
            extensionElements.setActivityListeners(
                    req.getListeners().stream()
                            .map(e -> getActivityListener(e))
                            .collect(Collectors.toList())
            );
        }

        sequenceFlow.setExtensionElements(extensionElements);
        return convert(sequenceFlow, sequenceFlow.getId(), req.getModelId(), JSONUtil.toJsonStr(req));
    }

    @Override
    public String convertXmlGateWay(GateWayQuery req) {
        ExtensionElements extensionElements = null;
        String type = req.getType();
        if (StrUtil.equals(type, GatewayType.EVENT_BASED.getValue())) {
            EventBasedGateway gateway = new EventBasedGateway().setId(req.getId()).setName(req.getName());
            if (StrUtil.isNotBlank(req.getDocumentation())) {
                gateway.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
            }
            if (CollUtil.isNotEmpty(req.getListeners())) {
                extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
                extensionElements.setActivityListeners(
                        req.getListeners().stream()
                                .map(e -> getActivityListener(e))
                                .collect(Collectors.toList())
                );
                gateway.setExtensionElements(extensionElements);
            }

            return convert(gateway, gateway.getId(), req.getModelId(), JSONUtil.toJsonStr(req));

        } else if (StrUtil.equals(type, GatewayType.EXCLUSIVE.getValue())) {
            ExclusiveGateway gateway = new ExclusiveGateway()
                    .setId(req.getId())
                    .setName(req.getName());
            if (StrUtil.isNotBlank(req.getDocumentation())) {
                gateway.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
            }
            if (CollUtil.isNotEmpty(req.getListeners())) {
                extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
                extensionElements.setActivityListeners(
                        req.getListeners().stream()
                                .map(e -> getActivityListener(e))
                                .collect(Collectors.toList())
                );
                gateway.setExtensionElements(extensionElements);
            }

            return convert(gateway, gateway.getId(), req.getModelId(), JSONUtil.toJsonStr(req));

        } else if (StrUtil.equals(type, GatewayType.INCLUSIVE.getValue())) {
            InclusiveGateway gateway = new InclusiveGateway()
                    .setId(req.getId())
                    .setName(req.getName());
            if (StrUtil.isNotBlank(req.getDocumentation())) {
                gateway.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
            }
            if (CollUtil.isNotEmpty(req.getListeners())) {
                extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
                extensionElements.setActivityListeners(
                        req.getListeners().stream()
                                .map(e -> getActivityListener(e))
                                .collect(Collectors.toList())
                );
                gateway.setExtensionElements(extensionElements);
            }
            return convert(gateway, gateway.getId(), req.getModelId(), JSONUtil.toJsonStr(req));

        } else if (StrUtil.equals(type, GatewayType.PARALLEL.getValue())) {
            ParallelGateway gateway = new ParallelGateway()
                    .setId(req.getId())
                    .setName(req.getName());
            if (StrUtil.isNotBlank(req.getDocumentation())) {
                gateway.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
            }
            if (CollUtil.isNotEmpty(req.getListeners())) {
                extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
                extensionElements.setActivityListeners(
                        req.getListeners().stream()
                                .map(e -> getActivityListener(e))
                                .collect(Collectors.toList())
                );
                gateway.setExtensionElements(extensionElements);
            }

            return convert(gateway, gateway.getId(), req.getModelId(), JSONUtil.toJsonStr(req));
        } else {
            Asserts.fail("[bpm.def.error.gateway.nomatch]!");
        }

        return null;
    }

    @Override
    public String convertXmlUserTask(TaskQuery req) {
        // 验证检查
        customValidate(req);

        UserTask task = new UserTask();
        task.setId(req.getId());
        task.setName(req.getName());
        if (StrUtil.isNotBlank(req.getDocumentation())){
            task.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
        }

        if (StrUtil.equals(req.getExecuteCategory(), ExecuteCategory.EXECUTE.getValue())) {
            TaskExecuteQuery taskExecute = req.getTaskExecute();
            // 表达式
            if (StrUtil.isNotBlank(taskExecute.getExpression())) {
                task.setAssignee(taskExecute.getExpression());
            }
            // 指定人
            if (StrUtil.isNotBlank(taskExecute.getAssignee())) {
                task.setAssignee(taskExecute.getAssignee());
            }

            if (StrUtil.isBlank(task.getAssignee())) {
                Asserts.fail("[bpm.xml.userTask.assignee]");
            }

        } else if (StrUtil.equals(req.getExecuteCategory(), ExecuteCategory.CANDIDATE.getValue())) {
            TaskCandidateQuery taskCandidate = req.getTaskCandidate();
            // 候选计算表达式通过监听器加上去
            if (StrUtil.isNotBlank(taskCandidate.getCandidateExpression())) {
                String candidateListenerName = "自动增加候选人表达式";
                // 通过执行开始监听器设置流程变量
                req.setListeners(addListenerReplace(req.getListeners(),
                        ListenerEvent.TASK_CREATE.getValue(),
                        ListenerType.DELEGATE_EXPRESSION.getValue(),
                        BpmConstant.TASK_CANDIDATE_EXPRESSION_LISTENER,
                        candidateListenerName,
                        true
                ));
            }

            // 设置候选人
            if (CollUtil.isNotEmpty(taskCandidate.getCandidateUser())) {
                task.setCandidateUsers(CollUtil.join(taskCandidate.getCandidateUser(), ","));
            }

            // 设置候选组
            List<String> candidateGroups = new ArrayList<>();
            if (CollUtil.isNotEmpty(taskCandidate.getCandidateOrg())) {
                candidateGroups.add(ExecuteType.getCandidateGroups(ExecuteType.CANDIDATE_ORG.getValue(), taskCandidate.getCandidateOrg()));
            }
            if (CollUtil.isNotEmpty(taskCandidate.getCandidateRole())) {
                candidateGroups.add(ExecuteType.getCandidateGroups(ExecuteType.CANDIDATE_ROLE.getValue(), taskCandidate.getCandidateRole()));
            }
            if (CollUtil.isNotEmpty(taskCandidate.getCandidateLevel())) {
                candidateGroups.add(ExecuteType.getCandidateGroups(ExecuteType.CANDIDATE_LEVEL.getValue(), taskCandidate.getCandidateLevel()));
            }
            if (CollUtil.isNotEmpty(taskCandidate.getCandidatePosition())) {
                candidateGroups.add(ExecuteType.getCandidateGroups(ExecuteType.CANDIDATE_POSITION.getValue(), taskCandidate.getCandidatePosition()));
            }
            if (!candidateGroups.isEmpty()) {
                task.setCandidateGroups(CollUtil.join(candidateGroups, ","));
            }

            if (StrUtil.isBlank(task.getCandidateGroups()) && StrUtil.isBlank(task.getCandidateUsers()) && StrUtil.isBlank(taskCandidate.getCandidateExpression())) {
                Asserts.fail("[bpm.xml.userTask.candidate]");
            }

        } else if (StrUtil.equals(req.getExecuteCategory(), ExecuteCategory.SIGN.getValue())) {
            boolean collectionEmpty = CollUtil.isEmpty(req.getCollection());
            boolean collectionExpressionBlank = StrUtil.isBlank(req.getCollectionExpression());
            if ((StrUtil.isBlank(req.getCompletionCondition()) && StrUtil.isBlank(req.getDefaultCompletionCondition())) ||
                    (collectionEmpty && collectionExpressionBlank)) {
                Asserts.fail("[bpm.xml.userTask.sign.param]");
            }

            if (StrUtil.isNotBlank(req.getDefaultCompletionCondition())) {
                // 转换默认结束表达式
                String varName = ActivityVariable.SIGN_AGREE_COUNT_KEY.getValue() + req.getId();
                req.setCompletionCondition(StrFormatter.formatWith("${() / nrOfInstances >= ()}", "()", varName, req.getDefaultCompletionCondition()));
            }

            if (req.isCompletionContinue()) {
                // 设置结束条件为全部审批完成
                req.setCompletionCondition(CounterSignExpression.ALL_COMPLETE.getValue());
            }

            // 会签
            MultiInstanceLoopCharacteristics m = new MultiInstanceLoopCharacteristics();
            m.setCollection(
                    StrFormatter.formatWith(
                            "${expressionListenerBean.getSignCollection(execution,'<>','<>','<>')}",
                            "<>",
                            RandomUtil.randomString(BpmConstant.ALL_LETTERS, 8), CollUtil.join(req.getCollection(), ","), req.getCollectionExpression()
                    )
            );

            if (StrUtil.isNotBlank(req.getCompletionCondition())) {
                m.setCompletionCondition(new CompletionCondition().setTextNode(new TextNode().setText(req.getCompletionCondition())));
            }
            m.setIsSequential(req.getIsSequential());
            m.setElementVariable("assign");
            task.setAssignee("${assign}");
            task.setMultiInstanceLoopCharacteristics(m);

        } else if (StrUtil.equals(req.getExecuteCategory(), ExecuteCategory.DYNAMIC.getValue())) {
            // 动态上级
            String dynamicApprovalType = req.getDynamicApprovalType();
            String dynamicApprovalValue = req.getDynamicApprovalValue();
            if (StrUtil.isBlank(dynamicApprovalType) && StrUtil.isBlank(dynamicApprovalValue)) {
                Asserts.fail("[bpm.xml.userTask.dynamic]");
            }
        }

        // 任务执行完成时间设置
        if (StrUtil.equals(req.getDueDateAction(), BpmConstant.TASK_DUE_DATE_ACTION_AUTORUN)) {
            // 时间格式转换
            String utcDatePattern = DateUtil.format(DateUtil.parse(req.getDueDate()), DatePattern.UTC_FORMAT);
            task.setDueDate(utcDatePattern);
        } else {
            // 默认不做任何反应
        }

        task.setFormKey(req.getFormKey());
        task.setPriority(req.getPriority());
        task.setSkipExpression(req.getSkipExpression());

        if (CollUtil.isNotEmpty(req.getFormProperties()) || CollUtil.isNotEmpty(req.getListeners())) {
            ExtensionElements extensionElements = new ExtensionElements();
            extensionElements.setFormProperties(req.getFormProperties());
            extensionElements.setActivityListeners(
                    req.getListeners().stream()
                            .map(e -> getActivityListener(e))
                            .collect(Collectors.toList())
            );

            task.setExtensionElements(extensionElements);
        }

        return convert(task, task.getId(), req.getModelId(), JSONUtil.toJsonStr(req));
    }

    private void customValidate(TaskQuery req) {
        // 用户任务三选一控制
//        checkThree(req);
    }

    private void checkThree(TaskQuery req) {
        int flag = 0;

        if (req.getTaskExecute() != null) {
            flag++;
        }
        if (StrUtil.isNotBlank(req.getDynamicApprovalType()) || StrUtil.isNotBlank(req.getDynamicApprovalValue())) {
            flag++;
        }
        if (StrUtil.isNotBlank(req.getIsSequential()) || CollUtil.isNotEmpty(req.getCollection())) {
            flag++;
        }

        if (flag > 1) {
            // 只能三选一
            Asserts.fail("[bpm.process.error.checkThree.two]");
        }
    }

    @Override
    public String convertXmlTimeBoundaryEvent(TimeBoundaryEventQuery req) {
        TimeBoundaryEvent boundaryEvent = new TimeBoundaryEvent();
        boundaryEvent.setId(req.getId());
        boundaryEvent.setName(req.getName());
        boundaryEvent.setAttachedToRef(req.getAttachedToRef());
        boundaryEvent.setCancelActivity(req.getCancelActivity());

        TimerEventDefinition definition = new TimerEventDefinition();
        int flag = 0;
        if (StrUtil.isNotBlank(req.getTimeCycle())) {
            definition.setTimeCycle(new TimeCycle().setTextNode(new TextNode().setText(req.getTimeCycle())));
            flag++;
        }

        if (StrUtil.isNotBlank(req.getTimeDuration())) {
            definition.setTimeDuration(new TimeDuration().setTextNode(new TextNode().setText(req.getTimeDuration())));
            flag++;
        }

        if (StrUtil.isNotBlank(req.getTimeDate())) {
            definition.setTimeDate(new TimeDate().setTextNode(new TextNode().setText(req.getTimeDate())));
            flag++;
        }

        if (flag == 0) {
            Asserts.fail("[bpm.def.error.timeout]");
        }

        boundaryEvent.setTimerEventDefinition(definition);

        if (StrUtil.isNotBlank(req.getDocumentation())) {
            boundaryEvent.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
        }

        ExtensionElements extensionElements = null;
        if (CollUtil.isNotEmpty(req.getListeners())) {
            extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
            extensionElements.setActivityListeners(
                    req.getListeners().stream()
                            .map(e -> getActivityListener(e))
                            .collect(Collectors.toList())
            );
        }
        boundaryEvent.setExtensionElements(extensionElements);

        return convert(boundaryEvent, boundaryEvent.getId(), req.getModelId(), JSONUtil.toJsonStr(req));
    }

    @Override
    public String convertXmlMessageBoundaryEvent(MessageBoundaryEventQuery req) {
        MessageBoundaryEvent boundaryEvent = new MessageBoundaryEvent();
        boundaryEvent.setId(req.getId());
        boundaryEvent.setName(req.getName());
        boundaryEvent.setAttachedToRef(req.getAttachedToRef());
        boundaryEvent.setCancelActivity(req.getCancelActivity());
        if (StrUtil.isNotBlank(req.getDocumentation())) {
            boundaryEvent.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
        }

        ExtensionElements extensionElements = null;
        if (CollUtil.isNotEmpty(req.getListeners())) {
            extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
            extensionElements.setActivityListeners(
                    req.getListeners().stream()
                            .map(e -> getActivityListener(e))
                            .collect(Collectors.toList())
            );
        }
        boundaryEvent.setExtensionElements(extensionElements);
        boundaryEvent.setMessageEventDefinition(new MessageEventDefinition().setMessageRef(req.getMessageRef()));

        return convert(boundaryEvent, boundaryEvent.getId(), req.getModelId(), JSONUtil.toJsonStr(req));
    }

    @Override
    public String convertXmlTerminateEndEvent(TerminateEndEventQuery req) {
        EndEvent endEvent = new EndEvent();
        endEvent.setId(req.getId());
        endEvent.setName(req.getName());
        if (StrUtil.isNotBlank(req.getDocumentation())) {
            endEvent.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
        }
        // 设置终结结束配置
        endEvent.setTerminateEventDefinition(new TerminateEventDefinition().setTerminateAll(req.isTerminateAll() + ""));

        // 增加一个流程终结监听器
        req.setListeners(addListener(req.getListeners(),
                ListenerEvent.EXECUTION_START.getValue(),
                ListenerType.DELEGATE_EXPRESSION.getValue(),
                BpmConstant.PROCESS_TERMINATE_END_CALL_BACK_DELEGATE_EXPRESSION,
                "流程终结监听器"
        ));

        ExtensionElements extensionElements = null;
        if (CollUtil.isNotEmpty(req.getListeners())) {
            extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
            extensionElements.setActivityListeners(
                    req.getListeners().stream()
                            .map(e -> getActivityListener(e))
                            .collect(Collectors.toList())
            );
        }
        endEvent.setExtensionElements(extensionElements);

        return convert(endEvent, endEvent.getId(), req.getModelId(), JSONUtil.toJsonStr(req));
    }

    @Override
    public String convertXmlCallActivity(CallActivityQuery req) {
        CallActivity callActivity = new CallActivity();
        callActivity.setId(req.getId());
        callActivity.setName(req.getName());
        callActivity.setCalledElement(req.getCalledElement());
        ExtensionElements extensionElements = null;
        if (CollUtil.isNotEmpty(req.getCallActivityIns())) {
            extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
            extensionElements.setCallActivityIns(req.getCallActivityIns());
        }
        if (CollUtil.isNotEmpty(req.getCallActivityOuts())) {
            extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
            extensionElements.setCallActivityOuts(req.getCallActivityOuts());
        }

        if (StrUtil.isNotBlank(req.getDocumentation())) {
            callActivity.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
        }
        if (CollUtil.isNotEmpty(req.getListeners())) {
            extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
            extensionElements.setActivityListeners(
                    req.getListeners().stream()
                            .map(e -> getActivityListener(e))
                            .collect(Collectors.toList())
            );
        }

        callActivity.setExtensionElements(extensionElements);
        return convert(callActivity, callActivity.getId(), req.getModelId(), JSONUtil.toJsonStr(req));
    }

    @Override
    public String convertXmlEndEvent(EndEventQuery req) {
        EndEvent endEvent = new EndEvent();
        endEvent.setId(req.getId());
        endEvent.setName(req.getName());
        if (StrUtil.isNotBlank(req.getDocumentation())) {
            endEvent.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
        }

        // 增加一个流程结束监听器
        req.setListeners(addListener(req.getListeners(),
                ListenerEvent.EXECUTION_END.getValue(),
                ListenerType.DELEGATE_EXPRESSION.getValue(),
                BpmConstant.PROCESS_END_CALL_BACK_DELEGATE_EXPRESSION,
                "流程正常结束监听器"
        ));

        ExtensionElements extensionElements = null;
        if (CollUtil.isNotEmpty(req.getListeners())) {
            extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
            extensionElements.setActivityListeners(
                    req.getListeners().stream()
                            .map(e -> getActivityListener(e))
                            .collect(Collectors.toList())
            );
        }
        endEvent.setExtensionElements(extensionElements);

        return convert(endEvent, endEvent.getId(), req.getModelId(), JSONUtil.toJsonStr(req));
    }

    @Override
    public void updateAfterDeploy(String modelId, String deploymentId) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        if (processDefinition == null) {
            Asserts.fail("[bpm.def.error.empty.processDef]");
        }

        // 更新json配置
        List<ActWorkflowConfigEntity> lastConfigs = list(Wrappers.lambdaQuery(ActWorkflowConfigEntity.class)
                .eq(ActWorkflowConfigEntity::getModelId, modelId)
                .eq(ActWorkflowConfigEntity::getDeleted, CommonConstant.YESNO_NO)
                .isNull(ActWorkflowConfigEntity::getProcDefId)
        );
        if (CollUtil.isNotEmpty(lastConfigs)) {
            lastConfigs.stream().forEach(e -> e.setProcDefId(processDefinition.getId()));
            // 关联proc_def_id
            updateBatchById(lastConfigs);

            // 新增最新数据到待修改
            lastConfigs.stream().forEach(e -> {
                e.setProcDefId(null);
                e.setId(null);
            });
            saveBatch(lastConfigs);
        }

    }

    @Override
    public String convertXmlProcess(ProcessQuery req) {
        Process process = new Process();
        process.setId(req.getId());
        process.setName(req.getName());
        if (StrUtil.isNotBlank(req.getDocumentation())) {
            process.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
        }

        // 开启支持跳过
        if (req.isEnableSkip()) {
            req.setListeners(addListener(req.getListeners(),
                    ListenerEvent.EXECUTION_START.getValue(),
                    ListenerType.DELEGATE_EXPRESSION.getValue(),
                    BpmConstant.SKIP_ABLE_LISTENER,
                    "开启支持跳过"
            ));
        }

        ExtensionElements extensionElements = null;
        if (CollUtil.isNotEmpty(req.getListeners())) {
            extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
            extensionElements.setActivityListeners(
                    req.getListeners().stream()
                            .map(e -> getActivityListener(e))
                            .collect(Collectors.toList())
            );
        }
        process.setExtensionElements(extensionElements);

        process.setCandidateStarterUsers(CollUtil.join(req.getCandidateStarterUsers(), ","));
        process.setCandidateStarterGroups(CollUtil.join(req.getCandidateStarterGroups(), ","));

        return convert(process, process.getId(), req.getModelId(), JSONUtil.toJsonStr(req));
    }

    @Override
    public String convertXmlTimeIntermediateEvent(TimeIntermediateEventQuery req) {
        TimeIntermediateEvent intermediateEvent = new TimeIntermediateEvent();
        intermediateEvent.setId(req.getId());
        intermediateEvent.setName(req.getName());

        TimerEventDefinition definition = new TimerEventDefinition();
        if (StrUtil.isNotBlank(req.getTimeCycle())) {
            definition.setTimeCycle(new TimeCycle().setTextNode(new TextNode().setText(req.getTimeCycle())));
        }

        if (StrUtil.isNotBlank(req.getTimeDuration())) {
            definition.setTimeDuration(new TimeDuration().setTextNode(new TextNode().setText(req.getTimeDuration())));
        }

        if (StrUtil.isNotBlank(req.getTimeDate())) {
            definition.setTimeDate(new TimeDate().setTextNode(new TextNode().setText(req.getTimeDate())));
        }

        intermediateEvent.setTimerEventDefinition(definition);

        if (StrUtil.isNotBlank(req.getDocumentation())) {
            intermediateEvent.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
        }

        ExtensionElements extensionElements = null;
        if (CollUtil.isNotEmpty(req.getListeners())) {
            extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
            extensionElements.setActivityListeners(
                    req.getListeners().stream()
                            .map(e -> getActivityListener(e))
                            .collect(Collectors.toList())
            );
        }
        intermediateEvent.setExtensionElements(extensionElements);

        return convert(intermediateEvent, intermediateEvent.getId(), req.getModelId(), JSONUtil.toJsonStr(req));
    }

    @Override
    public String convertXmlMessageIntermediateEvent(MessageIntermediateEventQuery req) {
        MessageIntermediateEvent intermediateEvent = new MessageIntermediateEvent();
        intermediateEvent.setId(req.getId());
        intermediateEvent.setName(req.getName());
        if (StrUtil.isNotBlank(req.getDocumentation())) {
            intermediateEvent.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
        }

        ExtensionElements extensionElements = null;
        if (CollUtil.isNotEmpty(req.getListeners())) {
            extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
            extensionElements.setActivityListeners(
                    req.getListeners().stream()
                            .map(e -> getActivityListener(e))
                            .collect(Collectors.toList())
            );
        }
        intermediateEvent.setExtensionElements(extensionElements);

        if (StrUtil.isNotBlank(req.getMessageRef())) {
            intermediateEvent.setMessageEventDefinition(new MessageEventDefinition().setMessageRef(req.getMessageRef()));
        }

        return convert(intermediateEvent, intermediateEvent.getId(), req.getModelId(), JSONUtil.toJsonStr(req));
    }

    @Override
    public String convertXmlSubProcess(SubProcessQuery req) {
        SubProcess subProcess = new SubProcess();
        subProcess.setId(req.getId());
        subProcess.setName(req.getName());
        if (StrUtil.isNotBlank(req.getDocumentation())) {
            subProcess.setDocumentation(new Documentation().setTextNode(new TextNode().setText(req.getDocumentation())));
        }
        ExtensionElements extensionElements = null;
        if (CollUtil.isNotEmpty(req.getListeners())) {
            extensionElements = Opt.ofNullable(extensionElements).orElse(new ExtensionElements());
            extensionElements.setActivityListeners(
                    req.getListeners().stream()
                            .map(e -> getActivityListener(e))
                            .collect(Collectors.toList())
            );
        }
        subProcess.setExtensionElements(extensionElements);

        return convert(subProcess, subProcess.getId(), req.getModelId(), JSONUtil.toJsonStr(req));
    }

    @Override
    public void clearModelCache(String modelId) {
        // 根据modelId清除缓存
        String key = StrUtil.format(RedisKeyConstant.MODEL_DATA_CACHE_KEY_FIND_BY_MODEL_ID, modelId);
        redisService.deleteByKeyPattern(key);
    }

    @Override
    public List<VariableVO> variable(VariableQuery req) {
        List<VariableVO> vos = new ArrayList<>();
        String type = req.getType();
        // 枚举遍历获取
        ActivityVariable[] values = ActivityVariable.values();
        for (ActivityVariable variable : values) {
            if (StrUtil.equals(variable.getType(), type)) {
                vos.add(new VariableVO()
                        .setName(variable.getDescription())
                        .setValue(variable.getValue())
                );
            }
        }

        if (StrUtil.equals(type, ActivityVariable.SIGN_ALL_COUNT_KEY.getType())) {
            // 会签结果特殊处理
            vos = handleSign(vos, req.getModelId(), req.getActivityId());
        }

        return vos;
    }

    @Resource
    private IActWorkflowConfigService actWorkflowConfigService;

    private List<VariableVO> handleSign(List<VariableVO> vos, String modelId, String flowId) {
        List<VariableVO> resp = new ArrayList<>(vos.size());
        // 获取合法的会签节点
        String key = StrUtil.format(RedisKeyConstant.MODEL_DATA_CACHE_KEY_FIND_BY_MODEL_ID, modelId);
        List<Object> nodes = redisService.getByKeyPattern(key);
        if (CollUtil.isEmpty(nodes)) {
            // 数据库获取模型
            List<ActWorkflowConfigEntity> dbExist = actWorkflowConfigService.list(Wrappers.lambdaQuery(ActWorkflowConfigEntity.class)
                    .eq(ActWorkflowConfigEntity::getModelId, modelId)
                    .eq(BaseEntity::getDeleted, CommonConstant.YESNO_NO)
                    .isNull(ActWorkflowConfigEntity::getProcDefId)
            );
            nodes = dbExist.stream().map(ActWorkflowConfigEntity::getJson).collect(Collectors.toList());
        }

        Map<String, JSONObject> jsonNodeMap = nodes.stream()
                .map(e -> JSONUtil.parseObj(e.toString()))
                .collect(Collectors.toMap(e -> e.getStr("id"), e -> e));

        // 获取所有连线
        List<SequenceFlowQuery> sequenceFlows = jsonNodeMap.entrySet().stream()
                .filter(entry -> entry.getKey().startsWith(BpmConstant.GENERATE_ID_PREFIX_SEQUENCE_FLOW))
                .map(e -> JSONUtil.toBean(e.getValue(), SequenceFlowQuery.class))
                .collect(Collectors.toList());

        Optional<JSONObject> find = sequenceFlows.stream()
                .filter(e -> StrUtil.equals(e.getId(), flowId))
                .findFirst()
                .map(e -> jsonNodeMap.get(e.getSourceRef()));

        if (!find.isPresent()) {
            return resp;
        }

        List<TaskQuery> signNodes = new ArrayList<>();
        JSONObject node = find.get();
        String id = node.getStr("id");
        if (StrUtil.startWith(id, BpmConstant.GENERATE_ID_PREFIX_USERTASK)) {
            // 用户任务
            TaskQuery taskQuery = JSONUtil.toBean(node, TaskQuery.class);
            // 是否为会签节点
            if (StrUtil.equals(taskQuery.getExecuteCategory(), ExecuteCategory.SIGN.getValue())) {
                signNodes.add(taskQuery);
            }

        } else if (StrUtil.startWith(id, BpmConstant.GENERATE_ID_PREFIX_EXCLUSIVE_GATEWAY)
                || StrUtil.startWith(id, BpmConstant.GENERATE_ID_PREFIX_INCLUSIVE_GATEWAY)
                || StrUtil.startWith(id, BpmConstant.GENERATE_ID_PREFIX_PARALLEL_GATEWAY)
                || StrUtil.startWith(id, BpmConstant.GENERATE_ID_PREFIX_EVENT_BASED_GATEWAY)
        ) {
            // 再往上找一层
            List<JSONObject> secondLevelSourceRefs = sequenceFlows.stream()
                    .filter(e -> StrUtil.equals(e.getTargetRef(), id))
                    .map(e -> jsonNodeMap.get(e.getSourceRef()))
                    .collect(Collectors.toList());

            for (JSONObject secondNode : secondLevelSourceRefs) {
                String secondId = secondNode.getStr("id");
                if (StrUtil.startWith(secondId, BpmConstant.GENERATE_ID_PREFIX_USERTASK)) {
                    // 用户任务
                    TaskQuery taskQuery = JSONUtil.toBean(secondNode, TaskQuery.class);
                    // 是否为会签节点
                    if (StrUtil.equals(taskQuery.getExecuteCategory(), ExecuteCategory.SIGN.getValue())) {
                        signNodes.add(taskQuery);
                    }
                }

            }

        }

        // 会签节点和流程变量嵌套循环
        for (TaskQuery signNode : signNodes) {
            for (VariableVO variable : vos) {
                resp.add(new VariableVO()
                        .setName(signNode.getName() + variable.getName())
                        .setValue(variable.getValue() + signNode.getId())
                );
            }
        }

        return resp;
    }

    @Nullable
    private String convert(ActivityXml activityXml, String id, String modelId, String jsonData) {
        try {
            // xml解析成string返回
            return activityXml.convert();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            Asserts.fail("[bpm.def.error.xml.convert]");
        }

        return null;
    }
}
